package com.ruoyi.web.controller.shop.service.Impl;

import com.ruoyi.common.constant.OrderConstants;
import com.ruoyi.common.entity.DO.order.OrderRefundDO;
import com.ruoyi.common.entity.DO.shop.ShopAppletDO;
import com.ruoyi.common.entity.VO.RefundVO;
import com.ruoyi.common.entity.VO.order.ShopOrderVO;
import com.ruoyi.common.entity.wxpay.WXPay;
import com.ruoyi.common.entity.wxpay.WXPayUtil;
import com.ruoyi.common.entity.wxpay.WxConfig;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.NumberUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.web.controller.order.mapper.IBackMemberRechargeOrderMapper;
import com.ruoyi.web.controller.order.mapper.IBackOrderMapper;
import com.ruoyi.web.controller.order.mapper.IBackOrderRefundMapper;
import com.ruoyi.web.controller.order.mapper.IBackShopPetrolOrderMapper;
import com.ruoyi.web.controller.shop.service.IBackShopAppletService;
import com.ruoyi.web.controller.shop.service.IBackShopOrderRefundService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.*;

@Service("IBackShopOrderRefundService")
public class BackShopOrderRefundServiceImpl implements IBackShopOrderRefundService {

    @Value("${wx.refund.memberNotifyUrl}")
    private String memberNotifyUrl;

    @Resource
    private IBackOrderMapper backOrderMapper;

    @Resource
    private IBackOrderRefundMapper backOrderRefundMapper;

    @Resource
    private IBackShopAppletService backShopAppletService;

    @Resource
    private IBackMemberRechargeOrderMapper backMemberRechargeOrderMapper;

    @Resource
    private IBackShopPetrolOrderMapper backShopPetrolOrderMapper;

    @Override
    public RefundVO getRefundInfo(String orderSn) {
        OrderRefundDO orderRefund = new OrderRefundDO();
        ShopOrderVO orderInfo = backOrderMapper.getDetailForPay(orderSn);
        if (orderInfo == null) {
            throw new BusinessException("订单不存在！");
        }
        if (orderInfo.getPayStatus() == OrderConstants.OrderPayStatusEnum.PRE_REFUND.getValue()) {
            throw new BusinessException("当前订单已申请退款，请耐心等待平台处理！");
        }
        if (orderInfo.getPayStatus() == OrderConstants.OrderPayStatusEnum.REFUNDING.getValue()) {
            throw new BusinessException("当前订单正在退款中，退款金额在1-5天内会原路返回！");
        }
        if (orderInfo.getPayStatus() == OrderConstants.OrderPayStatusEnum.REFUND_SUCCESS.getValue()) {
            throw new BusinessException("当前订单已退款成功，请勿重复操作！");
        }
        RefundVO refundVO = new RefundVO();
        refundVO.setShopId(orderRefund.getShopId());
        refundVO.setOrderSn(orderRefund.getRefundSn());
        refundVO.setPayWay(OrderConstants.OrderPayWayEnum.WX_PAY.getValue());
        refundVO.setPaySn(orderRefund.getRefundPaySn());
        refundVO.setPayCost(orderRefund.getOrderPayCost());
        refundVO.setRefundAmount(orderRefund.getRefundAmount());
        return refundVO;
    }

    @Override
    public Map<String, String> refund(RefundVO refundVO) {
        Map map = new LinkedHashMap<>();
        WXPay wxpay;
        String totalFee = String.valueOf(refundVO.getPayCost().multiply(new BigDecimal("100")).intValue());
        String refundFee = String.valueOf(refundVO.getRefundAmount().multiply(new BigDecimal("100")).intValue());
        map.put("out_trade_no", refundVO.getPaySn());
        map.put("out_refund_no", refundVO.getRefundSn());
        map.put("notify_url", memberNotifyUrl);
        map.put("total_fee", totalFee);
        map.put("refund_fee", refundFee);
        try {
            wxpay = new WXPay(refundVO.getWxConfig());
            Map resultMap = wxpay.refund(map);
            if (resultMap.get("return_msg").equals("OK") && resultMap.get("result_code").equals("SUCCESS")) {
                //退款中（这里其实已经可以确定退款成功了，但是最好还是等回调修改）
                backOrderRefundMapper.updateRefundStatusByRefundSn(refundVO.getRefundSn(), OrderConstants.RefundStatusEnum.REFUNDING.getValue());
                if (refundVO.getOrderType() == OrderConstants.RefundOrderTypeEnum.MEMBER_RECHARGE_ORDER.getValue()) {
                    backMemberRechargeOrderMapper.updatePayStatusByOrderSn(refundVO.getOrderSn(), OrderConstants.OrderPayStatusEnum.REFUND_SUCCESS.getValue());
                } else if (refundVO.getOrderType() == OrderConstants.RefundOrderTypeEnum.PETROL_ORDER.getValue()) {
                    backShopPetrolOrderMapper.updatePayStatusByOrderSn(refundVO.getOrderSn(), OrderConstants.OrderPayStatusEnum.REFUND_SUCCESS.getValue());
                }
                return map;
            } else {
                throw new BusinessException(resultMap.get("err_code_des").toString());
            }
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("微信退款失败，请联系客服处理！");
        }
    }

    @Override
    public Map<String, String> refresh(String refundSn, WxConfig wxConfig) {
        Map map = new LinkedHashMap<>();
        WXPay wxpay = null;
        map.put("out_refund_no", refundSn);
        try {
            wxpay = new WXPay(wxConfig);
            return wxpay.refundQuery(map);
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("微信退款失败，请联系客服处理！");
        }
    }

    @Override
    public List<OrderRefundDO> searchList(OrderRefundDO orderRefundDO) {
        Example example = new Example(OrderRefundDO.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("isDelete", 0);
        if (StringUtils.isNotBlank(orderRefundDO.getRefundOrderSn()))
        {
            criteria.andLike("refundOrderSn", "%" + orderRefundDO.getRefundOrderSn() + "%");
        }
        if (null != orderRefundDO.getRefundStatus() && -1 != orderRefundDO.getRefundStatus())
        {
            criteria.andEqualTo("refundStatus", orderRefundDO.getRefundStatus());
        }
        if (null != orderRefundDO.getOrderType() && -1 != orderRefundDO.getOrderType())
        {
            criteria.andEqualTo("orderType", orderRefundDO.getOrderType());
        }
        if (null != orderRefundDO.getShopId() && 0 != orderRefundDO.getShopId())
        {
            criteria.andEqualTo("shopId", orderRefundDO.getShopId());
        }
        if (StringUtils.isNotBlank(orderRefundDO.getRefundSn()))
        {
            criteria.andLike("refundSn", "%" + orderRefundDO.getRefundSn() + "%");
        }
        if (StringUtils.isNotBlank(orderRefundDO.getCreateBeginTime()))
        {
            criteria.andCondition("date_format(create_time,'%Y-%m-%d') >=",orderRefundDO.getCreateBeginTime());
        }
        if (StringUtils.isNotBlank(orderRefundDO.getCreateEndTime()))
        {
            criteria.andCondition("date_format(create_time,'%Y-%m-%d') <=",orderRefundDO.getCreateEndTime());
        }
        return backOrderRefundMapper.selectByExample(example);
    }

    @Override
    @Transactional
    public void refundNotifyHandle(Map<String, String> notifyMap) {
        BigDecimal refundAmount;
        if (!notifyMap.containsKey("refundSn") || !notifyMap.containsKey("refundPaySn")) {
            throw new BusinessException("退款回调处理失败！");
        }
        OrderRefundDO refundInfo = backOrderRefundMapper.getRefundInfoByRefundSn(notifyMap.get("refundSn"));
        if (refundInfo == null || !refundInfo.getRefundPaySn().equals(notifyMap.get("refundPaySn"))) {
            throw new BusinessException("订单不存在！");
        }
        //判断是退还余额还是退还微信
        if (!notifyMap.containsKey("wxAmount")) {
            throw new BusinessException("退款方式错误！");
        }
        refundAmount = new BigDecimal(notifyMap.get("wxAmount"));
        refundAmount = refundAmount.divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP);
        if (refundAmount.compareTo(refundInfo.getRefundAmount()) != 0) {
            throw new BusinessException("回调信息被篡改！");
        }
        if (refundInfo.getOrderType() == OrderConstants.RefundOrderTypeEnum.MEMBER_RECHARGE_ORDER.getValue()) {
            int exist = backMemberRechargeOrderMapper.checkIsValidRefund(refundInfo.getRefundOrderSn());
            if (exist == 0) {
                throw new BusinessException("订单不存在！");
            }
            backMemberRechargeOrderMapper.updatePayStatusByOrderSn(refundInfo.getRefundOrderSn(), OrderConstants.OrderPayStatusEnum.REFUND_SUCCESS.getValue());
        } else if (refundInfo.getOrderType() == OrderConstants.RefundOrderTypeEnum.PETROL_ORDER.getValue()) {
            int exist = backShopPetrolOrderMapper.checkIsValidRefund(refundInfo.getRefundOrderSn());
            if (exist == 0) {
                throw new BusinessException("订单不存在！");
            }
            backShopPetrolOrderMapper.updatePayStatusByOrderSn(refundInfo.getRefundOrderSn(), OrderConstants.OrderPayStatusEnum.REFUND_SUCCESS.getValue());
        }
        //TODO:变更退款申请的退款状态
        backOrderRefundMapper.updateRefundStatusByRefundSn(notifyMap.get("refundSn"), OrderConstants.RefundStatusEnum.COMPLETE.getValue());
    }

    @Override
    public String wxRefundNotifyHandle(HttpServletRequest request, String callBackClassName) {
        String errorXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>" + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
        String successXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
        try {
            Map<String, String> params = getWxPayNotifyParams(request);
            if (SpringUtils.containsBean(callBackClassName)) {
                Object callbackBean = SpringUtils.getBean(callBackClassName);
                Object[] notifyArr = new Object[1];
                Map<String, String> notifyMap = new HashMap<>();
                notifyMap.put("refundPaySn", params.get("out_trade_no"));
                notifyMap.put("refundSn", params.get("out_refund_no"));
                notifyMap.put("wxAmount", params.get("total_fee"));
                notifyArr[0] = notifyMap;
                ReflectUtils.invokeMethodByName(callbackBean, "refundNotifyHandle", notifyArr);
                return successXml;
            } else {
                return errorXml;
            }
        } catch (BusinessException e) {
            return errorXml;
        } catch (Exception e) {
            e.printStackTrace();
            return errorXml;
        }
    }

    private Map<String, String> getWxPayNotifyParams(HttpServletRequest request) {
        InputStream inputStream;
        try {
            inputStream = request.getInputStream();
        } catch (IOException e) {
            throw new BusinessException("读取回调数据流异常！");
        }
        //将InputStream转换成xmlString
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(sb.toString());
            Long shopId = Long.valueOf(String.valueOf(request.getAttribute("shopId")));
            //TODO：查询店铺的appId和secret
            ShopAppletDO shopApplet = backShopAppletService.getShopAppletInfoByShopId(shopId);
            WxConfig wxConfig = new WxConfig();
            wxConfig.setAppID(shopApplet.getAppletAppId());
            wxConfig.setKey(shopApplet.getAppletPayKey());
            wxConfig.setMchID(shopApplet.getAppletMchId());
            wxConfig.setCertData(shopId);
            WXPay wxpay = new WXPay(wxConfig);
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                if (!notifyMap.containsKey("return_code") || !notifyMap.get("return_code").equals("SUCCESS")) {
                    throw new BusinessException("手机支付回调通知签名错误！");
                }
                return notifyMap;
            } else {
                // 签名错误，如果数据里没有sign字段，也认为是签名错误
                throw new BusinessException("手机支付回调通知签名错误！");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException("微信配置异常！");
        }
    }

}
